package cn.edu.dgut.xhx.covid.service.impl;

import cn.edu.dgut.xhx.covid.constant.CovidConstant;
import cn.edu.dgut.xhx.covid.dao.CovidDao;
import cn.edu.dgut.xhx.covid.pojo.CovidPerson;
import cn.edu.dgut.xhx.covid.pojo.CovidPerson_;
import cn.edu.dgut.xhx.covid.pojo.dto.CovidQueryDTO;
import cn.edu.dgut.xhx.covid.service.CovidService;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class CovidServiceImpl implements CovidService {

    @Autowired
    private CovidDao covidDao;

    @Autowired
    private RestTemplate restTemplate;

    private static Map<String, Integer> map = new HashMap<>();

    static {
        map.put(CovidConstant.COVID_CONFIRMED_URL, CovidConstant.CONFIRMED);
        map.put(CovidConstant.COVID_DEATH_URL, CovidConstant.DEATH);
        map.put(CovidConstant.COVID_RECOVERED_URL, CovidConstant.RECOVERED);
    }


    @Override
    public Page<CovidPerson> getList(CovidQueryDTO covidQueryDTO, Pageable page) {

        return covidDao.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            if (!StringUtils.isEmpty(covidQueryDTO.getProvince())) {
                predicateList.add(criteriaBuilder.equal(root.get(CovidPerson_.PROVINCE), covidQueryDTO.getProvince()));
            }
            if (!StringUtils.isEmpty(covidQueryDTO.getCountry())) {
                predicateList.add(criteriaBuilder.equal(root.get(CovidPerson_.COUNTRY), covidQueryDTO.getCountry()));
            }
            if (covidQueryDTO.getDate() != null) {
                System.out.println(new Date(covidQueryDTO.getDate().getTime()));
                predicateList.add(criteriaBuilder.equal(root.get(CovidPerson_.DATE), new Date(covidQueryDTO.getDate().getTime())));
            }
            return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
        }, page);
    }

    @Override
    public void getData() {
        map.forEach((key, value) -> {
            Map<String, List<String>> table = getHeadAndBody(key);
            saveToDb(table, value);
        });
    }

    @Override
    public void updateData() {
        map.forEach((key, value) -> {
            Map<String, List<String>> table = getHeadAndBody(key);
            updateDb(table, value);
        });
    }


    /**
     * 吧html解析成 表格形式
     *
     * @param url
     * @return
     */
    private Map<String, List<String>> getHeadAndBody(String url) {
        // 请求网页
        String html = restTemplate.getForObject(url, String.class);
        Document doc = Jsoup.parse(html);
        // 解析网页标签,返回数据
        List<String> heads = doc.select(".js-file-line").select("th").eachText();
        Elements tds = doc.select("tbody").select(".js-file-line").select("td").not(".js-line-number");
        List<String> body = tds.stream().map(Element::text).collect(Collectors.toList());
        // 返回数据
        Map<String, List<String>> table = new HashMap<>();
        table.put(CovidConstant.HEAD, heads);
        table.put(CovidConstant.BODY, body);
        return table;
    }

    /**
     * 更新所有数据，包括最新的
     *
     * @param table
     * @param datatype
     */
    private void saveToDb(Map<String, List<String>> table, Integer datatype) {
        List<String> heads = table.get(CovidConstant.HEAD);
        List<String> body = table.get(CovidConstant.BODY);
        // 保存到数据库
        int basicDataIndex = 0;
        for (int i = 0; i < body.size(); i++) {

            if (i % heads.size() == 0) {
                //记录基础属性位置，越过前面的基本属性
                basicDataIndex = i;
                i = i + 3;
                continue;
            }
            saveOrUpdateToDb(heads, body, i, basicDataIndex, datatype);
        }
    }

    /**
     * 获取最新日期的数据更新
     *
     * @param table
     * @param datatype
     */
    private void updateDb(Map<String, List<String>> table, Integer datatype) {
        List<String> heads = table.get(CovidConstant.HEAD);
        List<String> body = table.get(CovidConstant.BODY);
        int firstAndFinalDateInterval = heads.size() - 2;
        int basicDataIndex = 0;
        for (int i = 0; i < body.size(); i++) {

            if (i % heads.size() == 0) {
                //记录基础属性位置，越过前面的基本属性
                basicDataIndex = i;
                i = i + firstAndFinalDateInterval;
                continue;
            }
            saveOrUpdateToDb(heads, body, i, basicDataIndex, datatype);
        }
    }


    private void saveOrUpdateToDb(List<String> heads, List<String> body, Integer i, Integer basicDataIndex, Integer datatype) {
        CovidPerson person = new CovidPerson();
        person.setProvince(body.get(basicDataIndex));
        person.setCountry(body.get(basicDataIndex + 1));
        person.setLatitude(body.get(basicDataIndex + 2));
        person.setLongitude(body.get(basicDataIndex + 3));
        person.setDate(new Date(getTimeStamp(heads, i)));
        Example example = Example.of(person);
        CovidPerson person2 = (CovidPerson) covidDao.findOne(example).orElse(null);
        if (person2 != null) {
            //设置属性,jpa的save必须保证是新对象才可以insert进去
            person.setCovidId(person2.getCovidId());
            person.setConfirmedCount(person2.getConfirmedCount());
            person.setDeathCount(person2.getDeathCount());
            person.setRecoveredCount(person2.getRecoveredCount());
        }
        person.setLastUpdateDate(new Date(getTimeStamp(heads, i)));
        if (i == basicDataIndex + 3 + 1) {
            person.setAddedCount(Long.parseLong(body.get(i)));
        } else {
            Long addedCount = Long.parseLong(body.get(i)) - Long.parseLong(body.get(i - 1));
            person.setAddedCount(addedCount);
        }
        // 设置确诊或死亡或治愈人数
        setCount(person, body, i, datatype);
        covidDao.save(person);
    }

    private long getTimeStamp(List<String> heads, Integer i) {
        //获取对应的时间
        int dateIndex = i % heads.size();
        String[] date = heads.get(dateIndex).split("/");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Integer.parseInt("20" + date[2]), Integer.parseInt(date[0]) - 1, Integer.parseInt(date[1]));
        return calendar.getTime().getTime();
    }

    private void setCount(CovidPerson person, List<String> body, Integer i, Integer datatype) {
        if (datatype == CovidConstant.CONFIRMED) {
            person.setConfirmedCount(Long.parseLong(body.get(i)));
        } else if (datatype == CovidConstant.DEATH) {
            person.setDeathCount(Long.parseLong(body.get(i)));
        } else if (datatype == CovidConstant.RECOVERED) {
            person.setRecoveredCount(Long.parseLong(body.get(i)));
        }
    }


}
